package net.howson.phil.datahiding.old;

import java.util.Random;

public class DefaultEncodingScheme implements EncodingScheme {

	private Random random;
	private static final byte TOP = (byte) 0xF0;

	private static final boolean[][] combinations = new boolean[][] {
			{ true, true, false }, { true, false, true },
			{ false, true, true }, { false, true, true } };

	public DefaultEncodingScheme(int randomSeed) {
		this.random = new Random(randomSeed);
	}

	@Override
	public int decode(int red, int green, int blue, int seedRed, int seedGreen, int seedBlue) {
		int comb = random.nextInt(4);
		boolean[] b = combinations[comb];
		int[] nibbles = new int[2];
		int i = 0;
		if (b[0]) {
			nibbles[i++] = red & 0x0F;
		}
		if (b[1]) {
			nibbles[i++] = green & 0x0F;
		}
		if (b[2]) {
			nibbles[i++] = blue & 0x0F;
		}

		int recoveredValue = (((nibbles[0] << 4) | nibbles[1]) & 0xFF) ^ (random.nextInt(256)-128);
		return recoveredValue;
	}

	@Override
	public int encode(int input, int red, int green, int blue) {
		int comb = random.nextInt(4);
		boolean[] b = combinations[comb];
		byte[] nibbles = getNibbles(input ^ (random.nextInt(256)-128));
		int i = 0;
		if (b[0]) {
			red = encodeBottomNibble((byte) red, nibbles[i++]);
		} else {
			red = encodeBottomNibble((byte) red, nibbles[0]);
		}
		if (b[1]) {
			green = encodeBottomNibble((byte) green, nibbles[i++]);
		} else {
			green = encodeBottomNibble((byte) green, nibbles[0]);
		}
		if (b[2]) {
			blue = encodeBottomNibble((byte) blue, nibbles[i++]);
		}else {
			blue = encodeBottomNibble((byte) blue, nibbles[1]);
		}
		
	
		return (red << 16) | (green << 8) | blue;
	}

	private byte[] getNibbles(int input) {
		input= input & 0xFF;
		byte[] b = new byte[] { (byte) ((input & 0xF0) >>> 4),
				(byte) (input & 0x0F) };
		return b;
	}

	private static int encodeBottomNibble(byte in, byte toEncode) {
		if (toEncode >= 16) {
			throw new IllegalArgumentException("Cannot encode > 16");
		}
		byte top = (byte) (in & TOP);
		int out = (top  | (toEncode &0x0F)) ;		
		return 128+out;
	}

	@Override
	public int getNumberOfBytesEncodable(int width, int height) {
		return width * height;
	}

}
